/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package desarrollosoft.ghce.servicio.implementaciones;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.PdfImportedPage;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfWriter;
import desarrollosoft.ghce.dao.interfaces.IAntecedenteHeredoFamiliarDAO;
import desarrollosoft.ghce.dao.interfaces.IAntecedenteNoPatologicoDAO;
import desarrollosoft.ghce.dao.interfaces.IAntecedentePatologicoDAO;
import desarrollosoft.ghce.dao.interfaces.IElementoDAO;
import desarrollosoft.ghce.entidades.AntecedenteHeredoFamiliar;
import desarrollosoft.ghce.entidades.AntecedenteNoPatologico;
import desarrollosoft.ghce.entidades.AntecedentePatologico;
import desarrollosoft.ghce.entidades.BajaLogicaPaciente;
import desarrollosoft.ghce.entidades.Consulta;
import desarrollosoft.ghce.entidades.Elemento;
import desarrollosoft.ghce.entidades.EstudioImaginologia;
import desarrollosoft.ghce.entidades.EstudioLaboratorio;
import desarrollosoft.ghce.entidades.Imagen;
import desarrollosoft.ghce.entidades.Intervencion;
import desarrollosoft.ghce.entidades.Paciente;
import desarrollosoft.ghce.entidades.ResultadoComponente;
import desarrollosoft.ghce.entidades.Tratamiento;
import desarrollosoft.ghce.servicio.interfaces.IServicioExportarHistoriaClinica;
import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

/**
 *
 * @author ariel
 */
@Service("servicioExportarHistoriaClinica")
public class ServicioExportarHistoriaClinica implements IServicioExportarHistoriaClinica {

    private static SimpleDateFormat formatoFecha = new SimpleDateFormat("dd/MM/yyyy");
    
    private static Font fuenteTitulo = new Font(Font.FontFamily.TIMES_ROMAN, 18, Font.NORMAL);
    private static Font fuenteSubtitulo = new Font(Font.FontFamily.TIMES_ROMAN, 16, Font.NORMAL);
    private static Font fuenteComun = new Font(Font.FontFamily.TIMES_ROMAN, 12, Font.NORMAL);
  
    private PdfWriter escritor;
    
    @Autowired
    @Qualifier("antecedentePatologicoDAO")
    private IAntecedentePatologicoDAO antecedentePatologicoDAO;
    
    @Autowired
    @Qualifier("antecedenteNoPatologicoDAO")
    private IAntecedenteNoPatologicoDAO antecedenteNoPatologicoDAO;
    
    @Autowired
    @Qualifier("antecedenteHeredoFamiliarDAO")
    private IAntecedenteHeredoFamiliarDAO antecedenteHeredoFamiliarDAO;
    
    @Autowired
    @Qualifier("elementoDAO")
    private IElementoDAO elementoDAO;
    
    @Override
    public File obtenerArchivoHistoriaClinica(Paciente paciente) throws Exception {
        String numeroPaciente = String.valueOf( paciente.getId() );
        String apellidos = paciente.getApellidos();
        String nombres = paciente.getNombres();
        
        String nombreArchivo = 
                "GHCE - 2014 - Paciente N° " + numeroPaciente + " " + apellidos + ", " + nombres + ".pdf";
        
        String directorioTemporal = System.getProperty("java.io.tmpdir");
        File archivoTemporal = new File(directorioTemporal, nombreArchivo);
        FileOutputStream fileOuputStream = new FileOutputStream(archivoTemporal); 
        crearDocumento(fileOuputStream, paciente);
        return archivoTemporal;
    }

    private void crearDocumento(FileOutputStream fileOuputStream, Paciente paciente) throws Exception {
        Document document = new Document();
        escritor = PdfWriter.getInstance(document, fileOuputStream);
        escritor.setStrictImageSequence(true); // para forzar a agregar las imagenes cuando se le indica
        ListenerEventosPagina listenerEventosPagina = new ListenerEventosPagina(); // listener para colocar numero de pagina
        escritor.setPageEvent(listenerEventosPagina);
        document.open();
        agregarContenido(document, paciente);
        document.close(); // cierra el documento y el archivo
    }

    private void agregarContenido(Document document, Paciente paciente) throws Exception {
        agregarTitulo(document, paciente);
        agregarDatosPersonales(document, paciente);
        if (paciente.getBajaLogicaPaciente() != null) {
            agregarDatosBajaLogica(document, paciente.getBajaLogicaPaciente());
        }
        agregarAntecedentes(document, paciente);
        agregarElementosHistorial(document, paciente);
    }

    private void agregarTitulo(Document document, Paciente paciente) throws DocumentException {
        Paragraph parrafoTitulo = new Paragraph("Historia Clinica N°" + paciente.getId(), fuenteTitulo);
        parrafoTitulo.setAlignment(Element.ALIGN_CENTER);
        nuevaLinea(parrafoTitulo, 1); // linea vacia
        document.add(parrafoTitulo);
        
        Paragraph parrafoSubtitulo = new Paragraph(
                "Historia Clinica exportada el dia " + formatoFecha.format(new Date()) + 
                        ", a partir del \"Sistema Gestor de Historias Clinicas - 2014\"",
                fuenteSubtitulo
        );
        parrafoSubtitulo.setAlignment(Element.ALIGN_CENTER);
        nuevaLinea(parrafoSubtitulo, 1); // linea vacia
        document.add(parrafoSubtitulo);
    }

    private void agregarDatosPersonales(Document document, Paciente paciente) throws DocumentException {
        Paragraph parrafoTitulo = new Paragraph();
        
        Paragraph titulo = new Paragraph("Datos Personales", fuenteTitulo);
        titulo.setAlignment(Element.ALIGN_CENTER);
        parrafoTitulo.add(titulo);
        
        nuevaLinea(parrafoTitulo, 1); // linea vacia
        document.add(parrafoTitulo);
        
        String contenido = 
                "Apellidos: " + paciente.getApellidos() + "\n"
                + "Nombres: " + paciente.getNombres() + "\n"
                + "Sexo: " + paciente.getSexo().getNombre() + "\n"
                + "Tipo Documento: " + paciente.getTipoDocumento().getNombre() + "\n"
                + "Numero Documento: " + paciente.getNumeroIdentificacion() + "\n"
                + "Fecha Nacimiento: " + formatoFecha.format( paciente.getFechaNacimiento() ) + "\n"
                + "Edad: " + obtenerEdad(paciente) + "\n" //con metodo especial, por si estaba dado de baja
                + "Ocupacion: " + ((paciente.getOcupacion() == null ) ? "-" : paciente.getOcupacion()) + "\n"
                + "Domicilio: " + ((paciente.getDomicilio() == null) ? "-" : paciente.getDomicilio()) + "\n";
        
        Paragraph parrafoContenido = new Paragraph(contenido, fuenteComun);
        nuevaLinea(parrafoContenido, 1); // linea vacia
        document.add(parrafoContenido);
    }
    
    private String obtenerEdad(Paciente paciente) {
        BajaLogicaPaciente baja = paciente.getBajaLogicaPaciente();
        if (baja == null) {
            return "" + paciente.getEdad() + " (actual)";
        }
        
        GregorianCalendar calendarNacimiento = new GregorianCalendar();
        calendarNacimiento.setTime(paciente.getFechaNacimiento());
        
        GregorianCalendar calendarActual = new GregorianCalendar();
        calendarActual.setTime(baja.getFechaSuceso()); // calculo la edad en la fecha de baja
        
        //Se restan la fecha actual y la fecha de nacimiento
        int anio = calendarActual.get(Calendar.YEAR)- calendarNacimiento.get(Calendar.YEAR);
        int mes = calendarActual.get(Calendar.MONTH)- calendarNacimiento.get(Calendar.MONTH);
        int dia = calendarActual.get(Calendar.DATE)- calendarNacimiento.get(Calendar.DATE);
        //Se ajusta el año dependiendo el mes y el día
        if (mes < 0 || ( mes == 0 && dia < 0) ){
            anio--;
        }
        //Regresa la edad en base a la fecha de nacimiento
        return "" + anio + " (en la fecha de Baja)";
    }

    private void agregarDatosBajaLogica(Document document, BajaLogicaPaciente bajaLogicaPaciente) throws DocumentException {
        Paragraph parrafoTitulo = new Paragraph();
        Paragraph titulo = new Paragraph("Datos Baja del Paciente", fuenteTitulo);
        titulo.setAlignment(Element.ALIGN_CENTER);
        parrafoTitulo.add(titulo);
        nuevaLinea(parrafoTitulo, 1); // linea vacia
        document.add(parrafoTitulo);
        
        String contenido = "Fecha Suceso: " + formatoFecha.format(bajaLogicaPaciente.getFechaSuceso()) + "\n"
               + "Causas: " + bajaLogicaPaciente.getCausas() + "\n";
        
        Paragraph parrafoContenido = new Paragraph(contenido, fuenteComun);
        nuevaLinea(parrafoContenido, 1); // linea vacia
        document.add(parrafoContenido);
    }

    private void agregarAntecedentes(Document document, Paciente paciente) throws DocumentException {
        Paragraph parrafoTitulo = new Paragraph();
        Paragraph titulo = new Paragraph("Antecedentes", fuenteTitulo);
        titulo.setAlignment(Element.ALIGN_CENTER);
        parrafoTitulo.add(titulo);
        nuevaLinea(parrafoTitulo, 1); // linea vacia
        document.add(parrafoTitulo);
        
        /* ANTECEDENTES PATOLOGICOS */
        Paragraph parrafoSubtituloAntecedentePatologico = new Paragraph("Antecedentes Patologicos", fuenteSubtitulo);
        parrafoSubtituloAntecedentePatologico.setAlignment(Element.ALIGN_CENTER);
        nuevaLinea(parrafoSubtituloAntecedentePatologico, 1); // linea vacia
        document.add(parrafoSubtituloAntecedentePatologico);
        
        // obtengo todos antecedentes patologicos
        List<AntecedentePatologico> listaAntecedentesPatologicos =
                antecedentePatologicoDAO.obtenerTodosAntecedentesPatologicos(paciente, null);
        
        // recorro la lista y voy pasando a string
        for (AntecedentePatologico antecedentePatologico : listaAntecedentesPatologicos) {
            String contenido = "Fecha: " + formatoFecha.format(antecedentePatologico.getFecha()) + "\n"
                    + "Diagnostico: " + antecedentePatologico.getDiagnostico().getNombre() + "\n"
                    + "Edad en el Momento: " + antecedentePatologico.getEdadEnElMomento() + "\n"
                    + "Gravedad: " + antecedentePatologico.getGravedad().getNombre() + "\n"
                    + "Fecha Fin: " + ((antecedentePatologico.getFechaFin() == null) ? "-" : formatoFecha.format(antecedentePatologico.getFechaFin())) + "\n"
                    + "Observaciones: " + antecedentePatologico.getObservacion() + "\n";
            
            Paragraph parrafoContenido = new Paragraph(contenido, fuenteComun);
            nuevaLinea(parrafoContenido, 1); // linea vacia
            document.add(parrafoContenido);
        }
        
        /* ANTECEDENTES NO PATOLOGICOS */
        Paragraph parrafoSubtituloAntecedenteNoPatologico = new Paragraph("Antecedentes NO Patologicos", fuenteSubtitulo);
        parrafoSubtituloAntecedenteNoPatologico.setAlignment(Element.ALIGN_CENTER);
        nuevaLinea(parrafoSubtituloAntecedenteNoPatologico, 1); // linea vacia
        document.add(parrafoSubtituloAntecedenteNoPatologico);
        
        // obtengo todos antecedentes NO patologicos
        List<AntecedenteNoPatologico> listaAntecedentesNoPatologicos = 
                antecedenteNoPatologicoDAO.obtenerAntecedentesNoPatologicos(paciente);
        
        // recorro la lista y voy pasando a string
        for (AntecedenteNoPatologico antecedenteNoPatologico : listaAntecedentesNoPatologicos) {
            String contenido = "Aspecto: " + antecedenteNoPatologico.getAspecto().getNombre() + "\n"
                    + "Fecha ultima modificacion: " + formatoFecha.format(antecedenteNoPatologico.getFechaUltimaModificacion()) + "\n"
                    + "Observacion: " + antecedenteNoPatologico.getObservacion() + "\n";
            
            Paragraph parrafoContenido = new Paragraph(contenido, fuenteComun);
            nuevaLinea(parrafoContenido, 1); // linea vacia
            document.add(parrafoContenido);
        }
        
        /* ANTECEDENTES HEREDO - FAMILIARES */
        Paragraph parrafoSubtituloAntecedenteHeredoFamiliar = new Paragraph("Antecedentes Heredo - Familiares", fuenteSubtitulo);
        parrafoSubtituloAntecedenteHeredoFamiliar.setAlignment(Element.ALIGN_CENTER);
        nuevaLinea(parrafoSubtituloAntecedenteHeredoFamiliar, 1); // linea vacia
        document.add(parrafoSubtituloAntecedenteHeredoFamiliar);
        
        // obtengo todos antecedentes Heredo - Familiares
        List<AntecedenteHeredoFamiliar> listaAntecedentesHeredoFamiliares = 
                antecedenteHeredoFamiliarDAO.obtenerAntecedentesHeredoFamiliares(paciente);
        
        // recorro la lista y voy pasando a string
        for (AntecedenteHeredoFamiliar antecedenteHeredoFamiliar : listaAntecedentesHeredoFamiliares) {
            String contenido = "Diagnostico: " + antecedenteHeredoFamiliar.getDiagnostico().getNombre() + "\n"
                    + "Observaciones: " + antecedenteHeredoFamiliar.getObservacion() + "\n";
            
            Paragraph parrafoContenido = new Paragraph(contenido, fuenteComun);
            nuevaLinea(parrafoContenido, 1); // linea vacia
            document.add(parrafoContenido);
        }
    }

    private void agregarElementosHistorial(Document document, Paciente paciente) throws Exception {
        Paragraph parrafoTitulo = new Paragraph();
        Paragraph titulo = new Paragraph("Historial", fuenteTitulo);
        titulo.setAlignment(Element.ALIGN_CENTER);
        parrafoTitulo.add(titulo);
        nuevaLinea(parrafoTitulo, 1); // linea vacia
        document.add(parrafoTitulo);
        
        // obtengo todos los elementos del historial de paciente
        List<Elemento> listaElementos = elementoDAO.buscarElementos(paciente, "", null);
        boolean agregado;
        // recorro la lista y voy pasando a String, segun el caso
        for (Elemento elemento : listaElementos) {
            agregado = false;
            String contenido = "Fecha: " + formatoFecha.format( elemento.getFecha() ) + "\n"
                    + "Tipificacion: " + elemento.getTipificacion() + "\n"
                    + "Descripcion: " + elemento.getDescripcion() + "\n"
                    + "Edad en el Momento: " + elemento.getEdadEnElMomento() + "\n"
                    + "Fecha Fin: " + ( (elemento.getFechaFin() == null) ? "-" : formatoFecha.format(elemento.getFechaFin()) ) + "\n";
            
            if (elemento instanceof Consulta) {
                Consulta consulta = (Consulta) elemento;
                contenido += "Anamnesis: " + consulta.getAnamnesis() + "\n"
                        + "Examen fisico: " + ((consulta.getExamenFisico() == null) ? "-" : consulta.getExamenFisico()) + "\n"
                        + "Diagnostico: " + ((consulta.getDiagnostico() == null) ? "-" : consulta.getDiagnostico().getNombre()) + "\n";
            } else if (elemento instanceof Tratamiento) {
                Tratamiento tratamiento = (Tratamiento) elemento;
                contenido += "Medicamento: " + ( (tratamiento.getMedicamento() == null) ? "-" : tratamiento.getMedicamento().getNombre()) + "\n"
                        + "Observaciones: " + tratamiento.getObservaciones() + "\n";
            } else if (elemento instanceof Intervencion) {
                Intervencion intervencion = (Intervencion) elemento;
                contenido += "Cirugia: " + intervencion.getCirugia().getNombre() + "\n"
                        + "Diagnostico: " + ((intervencion.getDiagnostico() == null) ? "-" : intervencion.getDiagnostico().getNombre()) + "\n"
                        + "Observaciones: " + intervencion.getObservaciones() + "\n";
            } else if (elemento instanceof EstudioLaboratorio) {
                EstudioLaboratorio estudioLaboratorio = (EstudioLaboratorio) elemento;
                List<ResultadoComponente> listaResultados = estudioLaboratorio.getListaResultadoComponente();
                contenido += "Detalles:\n";
                PdfPTable tabla = new PdfPTable(3); // 3 columnas
                tabla.setWidthPercentage(100);
                float[] anchosColumnas = {1f, 0.5f, 1f};
                tabla.setWidths(anchosColumnas);
                tabla.addCell(new PdfPCell(new Paragraph("Componente")));
                tabla.addCell(new PdfPCell(new Paragraph("Valor")));
                tabla.addCell(new PdfPCell(new Paragraph("U. Medida")));
                for (ResultadoComponente resultadoComponente : listaResultados) {
                    tabla.addCell(new PdfPCell(new Paragraph(resultadoComponente.getComponente().getNombre())));
                    tabla.addCell(new PdfPCell(new Paragraph(((resultadoComponente.getValor() == null) ? "-" : String.valueOf(resultadoComponente.getValor())))));
                    tabla.addCell(new PdfPCell(new Paragraph(resultadoComponente.getComponente().getUnidadMedida().toString())));
                }
                /* primero agrego el parrafo */
                Paragraph parrafoContenido = new Paragraph(contenido, fuenteComun);
                nuevaLinea(parrafoContenido, 1); // linea vacia
                document.add(parrafoContenido);
                agregado = true; // para que sepa que ya agregue el parrafo y no lo agregue de nuevo
                /* luego, agrego la tabla al documento */
                document.add(tabla);
                document.add(new Paragraph(" ",fuenteComun)); // linea vacia
            } else if (elemento instanceof EstudioImaginologia) {
                EstudioImaginologia estudioImaginologia = (EstudioImaginologia) elemento;
                if (estudioImaginologia.getInforme() != null) {
                    contenido += "Informe Textual:" + estudioImaginologia.getInforme();
                    /* agrego el parrafo */
                    Paragraph parrafoContenido = new Paragraph(contenido, fuenteComun);
                    nuevaLinea(parrafoContenido, 1); // linea vacia
                    document.add(parrafoContenido);
                    agregado = true; // para que sepa que ya agregue el parrafo y no lo agregue de nuevo
                }
                byte[] bytesInforme = estudioImaginologia.getArchivoInforme();
                if (bytesInforme != null) { // hay archivo
                    contenido += "Archivo Informe:\n";
                    /* agrego el parrafo */
                    Paragraph parrafoContenido = new Paragraph(contenido, fuenteComun);
                    document.add(parrafoContenido);
                    document.add(importarPdf(estudioImaginologia.getArchivoInforme()));
                    agregado = true;
                }
                List<Imagen> listaImagenes = estudioImaginologia.getListaImagenes();
                if (listaImagenes != null) {
                    int contador = 1;
                    for (Imagen imagen : listaImagenes) {
                        Image image = Image.getInstance(imagen.getArchivoImagen());
                        if (image.getScaledWidth() > 400 || image.getScaledHeight() > 400) {
                            image.scaleToFit(400, 400);
                        }
                        document.add(new Paragraph("Imagen " + contador + " de " + listaImagenes.size(), fuenteComun));
                        document.add(new Paragraph("Titulo: " + imagen.getTitulo(), fuenteComun));
                        document.add(new Paragraph("Comentario: " + imagen.getComentario(), fuenteComun));
                        document.add(new Paragraph("Imagen:", fuenteComun));
                        document.add(image);
                        document.add(new Paragraph(" "));
                        contador++;
                    }
                }
            }

            if (!agregado) {
                Paragraph parrafoContenido = new Paragraph(contenido, fuenteComun);
                nuevaLinea(parrafoContenido, 1); // linea vacia
                document.add(parrafoContenido);
            }
        }
    }
    
    private static void nuevaLinea(Paragraph paragraph, int lineas) {
        for (int i = 0; i < lineas; i++) {
            paragraph.add(new Paragraph(" "));
        }
    }

    private PdfPTable importarPdf(byte[] archivoInforme) throws Exception {
        PdfPTable tabla = new PdfPTable(1);
        PdfReader reader = new PdfReader(archivoInforme);
        int cantidadPaginasArchivoInforme = reader.getNumberOfPages();
        PdfImportedPage page;
        for (int i = 1; i <= cantidadPaginasArchivoInforme; i++) {
            page = escritor.getImportedPage(reader, i);
            tabla.addCell(Image.getInstance(page));
        }
        reader.close();
        return tabla;
    }
}
